/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package configs

import (
	"tmlake.com/api-gateway/istio-agent/proxy/njetcontroller/authtls"
	"tmlake.com/api-gateway/istio-agent/proxy/njetcontroller/jwt"
	"tmlake.com/api-gateway/istio-agent/proxy/njetcontroller/proxyssl"
)

var (
	// DefaultSSLDirectory defines the location where the SSL certificates will be generated
	// This directory contains all the SSL certificates that are specified in Ingress rules.
	// The name of each file is <namespace>-<secret name>.pem. The content is the concatenated
	// certificate and key.
	DefaultSSLDirectory = "/ingress-controller/ssl"
)

// Configuration holds the definition of all the parts required to describe all
// xds reachable by the njet controller
type Configuration struct {
	// Backends are a list of backends. This list includes the default backend
	Backends []*Backend `json:"backends,omitempty"`
	// Servers save the website config
	Servers []*VirtualServerConfig `json:"servers,omitempty"`
	// TCPL4Services contain tcp streams
	TCPL4Services map[string]*L4Service
	// UDPL4Services contain udp streams
	UDPL4Services []L4Service `json:"udpL4Services,omitempty"`
	// PassthroughBackends contains the backends used for SSL passthrough.
	// It contains information about the associated Server Name Indication (SNI).
	// +optional
	PassthroughBackends []*SSLPassthroughBackend `json:"passthroughBackends,omitempty"`

	// BackendConfigChecksum contains the particular checksum of a Configuration object
	BackendConfigChecksum string `json:"BackendConfigChecksum,omitempty"`

	// ConfigurationChecksum contains the particular checksum of a Configuration object
	ConfigurationChecksum string `json:"configurationChecksum,omitempty"`

	StreamSnippets []string
}

// Backend describes one or more remote server/s (endpoints) associated with a service
type Backend struct {
	// Name represents an unique CDS clustername formatted as <traffic_direction>|<port>|<micro-service version>|<k8s service domain name> eg:outbound|9080|v3|reviews.default.svc.cluster.local
	Name string `json:"name"`
	// SSLPassthrough indicates that Ingress controller will delegate TLS termination to the endpoints.
	SSLPassthrough bool `json:"sslPassthrough"`
	// Endpoints contains the list of endpoints currently running
	Endpoints []Endpoint `json:"endpoints,omitempty"`
	// StickySessionAffinitySession contains the StickyConfig object with stickiness configuration
	SessionAffinity SessionAffinityConfig `json:"sessionAffinityConfig"`
	// LB algorithm configuration per ingress
	LoadBalancing string `json:"load-balance,omitempty"`
	// Denotes if a backend has no server. The backend instead shares a server with another backend and acts as an
	// alternative backend.
	// This can be used to share multiple upstreams in the same njet server block.
	NoServer bool `json:"noServer"`
	// Policies to describe the characteristics of an alternative backend. Implementing traffic splitting in lua is used
	// +optional
	TrafficShapingPolicy TrafficShapingPolicy `json:"trafficShapingPolicy,omitempty"`
	// Contains a list of backends without servers that are associated with this backend.
	// +optional
	AlternativeBackends []string `json:"alternativeBackends,omitempty"`
	// ProxySSL indicates the this server uses client certificate to access backends
	// +optional
	ProxySSL proxyssl.Config `json:"proxySSL"`
	//SSLCert describes the certificate that will be used
	SSLCert *SSLCert
	SSLMode ClientTLSMode
}

// TrafficShapingPolicy describes the policies to put in place when a backend has no server and is used as an
// alternative backend
// use by lua
type TrafficShapingPolicy struct {
	// Weight (0-<WeightTotal>) of traffic to redirect to the backend.
	// e.g. <WeightTotal> defaults to 100, weight 20 means 20% of traffic will be
	// redirected to the backend and 80% will remain with the other backend. If
	// <WeightTotal> is set to 1000, weight 2 means 0.2% of traffic will be
	// redirected to the backend and 99.8% will remain with the other backend.
	// 0 weight will not send any traffic to this backend
	Weight int `json:"weight"`
	// The total weight of traffic (>= 100). If unspecified, it defaults to 100.
	WeightTotal int `json:"weightTotal"`
	// Header on which to redirect requests to this backend
	Header string `json:"header"`
	// HeaderValue on which to redirect requests to this backend
	HeaderValue string `json:"headerValue"`
	// HeaderPattern the header value match pattern, support exact, regex.
	HeaderPattern string `json:"headerPattern"`
	// Cookie on which to redirect requests to this backend
	Cookie string `json:"cookie"`

	Headers []map[string]string `json:"headers"`
}

// HashInclude defines if a field should be used or not to calculate the hash
func (s Backend) HashInclude(field string, v interface{}) (bool, error) {
	switch field {
	case "Endpoints":
		return false, nil
	default:
		return true, nil
	}
}

// SessionAffinityConfig describes different affinity configurations for new sessions.
// Once a session is mapped to a backend based on some affinity setting, it
// retains that mapping till the backend goes down, or the ingress controller
// restarts. Exactly one of these values will be set on the upstream, since multiple
// affinity values are incompatible. Once set, the backend makes no guarantees
// about honoring updates.
// +k8s:deepcopy-gen=true
type SessionAffinityConfig struct {
	AffinityType          string                `json:"name"`
	AffinityMode          string                `json:"mode"`
	CookieSessionAffinity CookieSessionAffinity `json:"cookieSessionAffinity"`
}

// CookieSessionAffinity defines the structure used in Affinity configured by Cookies.
// +k8s:deepcopy-gen=true
type CookieSessionAffinity struct {
	Name                    string              `json:"name"`
	Expires                 string              `json:"expires,omitempty"`
	MaxAge                  string              `json:"maxage,omitempty"`
	Locations               map[string][]string `json:"locations,omitempty"`
	Secure                  bool                `json:"secure,omitempty"`
	Path                    string              `json:"path,omitempty"`
	SameSite                string              `json:"samesite,omitempty"`
	ConditionalSameSiteNone bool                `json:"conditional_samesite_none,omitempty"`
	ChangeOnFailure         bool                `json:"change_on_failure,omitempty"`
}

// Endpoint describes a endpoint in a backend
type Endpoint struct {
	// Address IP address of the endpoint
	Address string `json:"address"`
	// Port number of the TCP port
	Port string `json:"port"`
}

// SplitClient defines a split_clients.
type SplitClient struct {
	Source        string
	Variable      string
	Distributions []Distribution
}

// Distribution maps weight to a value in a SplitClient.
type Distribution struct {
	Weight string
	Value  string
}

// InternalRedirectLocation defines a location for internally redirecting requests to named locations.
type InternalRedirectLocation struct {
	Path        string
	Destination string
}

// Map defines a map.
type Map struct {
	Source     string      `json:"source"`
	Variable   string      `json:"variable"`
	Parameters []Parameter `json:"parameters"`
}

// Parameter defines a Parameter in a Map.
type Parameter struct {
	Value  string `json:"value"`
	Result string `json:"result"`
}

// VirtualServerConfig holds NGINX configuration for a VirtualServer.
type VirtualServerConfig struct {
	HTTPSnippets []string
	Maps         []Map
	Server       *Server
	SpiffeCerts  bool
	SplitClients []SplitClient
	Name         string
	MergeRoutes  map[string]*Route
}

// Server describes a website,Generates an http server block for nginx.
//Corresponds to VirtualHost in XDS
type Server struct {
	//The logical name of the virtual host
	Name string `json:"name"`
	//The name of the route configuration of XDS. it might match route_config_name in extensions.filters.network.http_connection_manager.v3.Rds.
	RouteName string `json:"routeName"`
	// Hostname returns the FQDN of the server
	Hostnames []string `json:"hostnames"`
	// SSLPassthrough indicates if the TLS termination is realized in
	// the server or in the remote endpoint
	SSLPassthrough bool `json:"sslPassthrough"`
	// SSLCert describes the certificate that will be used on the server
	SSLCert *SSLCert `json:"sslCert"`
	// Locations list of URIs configured in the server.
	Locations                 []*Location                `json:"locations,omitempty"`
	InternalRedirectLocations []InternalRedirectLocation `json:"internalRedirectLocations,omitempty"`
	// Aliases return the alias of the server name
	Aliases []string `json:"aliases,omitempty"`
	// RedirectFromToWWW returns if a redirect to/from prefix www is required
	RedirectFromToWWW bool `json:"redirectFromToWWW,omitempty"`
	// CertificateAuth indicates the this server requires mutual authentication
	// +optional
	CertificateAuth authtls.Config `json:"certificateAuth"`
	// ServerSnippet returns the snippet of server
	// +optional
	ServerSnippet string `json:"serverSnippet"`
	// SSLCiphers returns list of ciphers to be enabled
	SSLCiphers string `json:"sslCiphers,omitempty"`
	// SSLPreferServerCiphers indicates that server ciphers should be preferred
	// over client ciphers when using the SSLv3 and TLS protocols.
	SSLPreferServerCiphers string `json:"sslPreferServerCiphers,omitempty"`
	// AuthTLSError contains the reason why the access to a server should be denied
	AuthTLSError string `json:"authTLSError,omitempty"`
}

type PathType string

const (
	// PathTypeExact matches the URL path exactly and with case sensitivity.
	PathTypeExact = PathType("Exact")

	// PathTypePrefix matches based on a URL path prefix split by '/'. Matching
	// is case sensitive and done on a path element by element basis. A path
	// element refers to the list of labels in the path split by the '/'
	// separator. A request is a match for path p if every p is an element-wise
	// prefix of p of the request path. Note that if the last element of the
	// path is a substring of the last element in request path, it is not a
	// match (e.g. /foo/bar matches /foo/bar/baz, but does not match
	// /foo/barbaz). If multiple matching paths exist in an Ingress spec, the
	// longest matching path is given priority.
	// Examples:
	// - /foo/bar does not match requests to /foo/barbaz
	// - /foo/bar matches request to /foo/bar and /foo/bar/baz
	// - /foo and /foo/ both match requests to /foo and /foo/. If both paths are
	//   present in an Ingress spec, the longest matching path (/foo/) is given
	//   priority.
	PathTypePrefix = PathType("Prefix")

	PathTypeSafeRegex = PathType("Regex")
)

type HTTPMatchRequest struct {
	// match the :path header, support 'prefix' and 'regex' and 'exact'
	Path string `json:"path"`
	// PathType represents the type of path referred to by a HTTPIngressPath.
	PathType *PathType `json:"pathType"`
	//Specifies a set of headers that the route should match on
	Headers map[string]interface{} `json:"headers"`
	// map.value If specified to true, the match result will be inverted before checking.
	WithoutHeaders map[string]bool
	// Query parameters for matching.
	//Specifies a set of URL query parameters on which the route should match.
	QueryParams map[string]*string `json:"queryParams"`
	// Flag to specify whether the Path matching should be case-insensitive.
	//
	// **Note:** The case will be ignored only in the case of `exact` and `prefix`
	// URI matches.
	IgnorePathCase bool `json:"ignorePathCase"`
}

// Describes the weight of an upstream cluster
type BackendWeight struct {
	Name   string
	Weight uint32
}

// Multiple upstream clusters can be specified for a given route.
// The request is routed to one of the upstream clusters based on weights assigned to each cluster.
// Implement traffic splitting
type WeightedBackend struct {
	Backends    []*BackendWeight
	TotalWeight uint32
}

type HTTPRouteDestination struct {
	// Indicates the upstream cluster to which the request should be routed to
	Backend string

	WeightedBackend *WeightedBackend
}

// Location describes an route inside a server.
//A route is both a specification of how to match a request as well as an indication of what to do next (e.g., redirect, forward, rewrite, etc.).
type Location struct {
	Path         string
	Internal     bool
	UpstreamName string
	// Overwrite the Host header passed into the backend. Defaults to
	// vhost of the incoming request.
	// +optional
	UpstreamVhost string `json:"upstream-vhost"`
	// Denied returns an error when this location cannot not be allowed
	// Requesting a denied location should return HTTP code 403.
	Denied *string `json:"denied,omitempty"`
	// HTTP2PushPreload allows to configure the HTTP2 Push Preload from backend
	// original location.
	// +optional
	HTTP2PushPreload bool `json:"http2PushPreload,omitempty"`
	// ConfigurationSnippet contains additional configuration for the backend
	// to be considered in the configuration of the location
	ConfigurationSnippet string `json:"configurationSnippet"`
	// ClientBodyBufferSize allows for the configuration of the client body
	// buffer size for a specific location.
	// +optional
	ClientBodyBufferSize string `json:"clientBodyBufferSize,omitempty"`
	// BackendProtocol indicates which protocol should be used to communicate with the service
	// By default this is HTTP
	BackendProtocol string `json:"backend-protocol"`
}

// SSLPassthroughBackend describes a SSL upstream server configured
// as passthrough (no TLS termination in the ingress controller)
// The endpoints must provide the TLS termination exposing the required SSL certificate.
// The ingress controller only pipes the underlying TCP connection
type SSLPassthroughBackend struct {
	// Backend describes the endpoints to use.
	Backend string `json:"namespace,omitempty"`
	// Hostname returns the FQDN of the server
	Hostname string `json:"hostname"`
}

type PeerAuthenticationMode string

const (
	UNSET      PeerAuthenticationMode = "UNSET"
	DISABLE    PeerAuthenticationMode = "DISABLE"
	PERMISSIVE PeerAuthenticationMode = "PERMISSIVE"
	STRICT     PeerAuthenticationMode = "STRICT"
)

type ClientTLSMode string

const (
	Client_SIMPLE       ClientTLSMode = "SIMPLE"
	Client_DISABLE      ClientTLSMode = "DISABLE"
	Client_MUTUAL       ClientTLSMode = "MUTUAL"
	Client_ISTIO_MUTUAL ClientTLSMode = "ISTIO_MUTUAL"

	Client_MUTUAL_Workload_Prefix = "file-cert:"
	Client_MUTUAL_Root_Prefix     = "file-root:"
)

const (
	Tls       = "tls"
	RawBuffer = "raw_buffer"
)

// L4Service describes a L4 service.
type L4Service struct {
	// Port external port to expose
	Port int `json:"port"`
	// Endpoints active endpoints of the service
	Endpoints []Endpoint `json:"endpoints,omitempETty"`
	// SSLCert describes the certificate that will be used on the server
	SSLCert map[string]*SSLCert
	//CertificateAuth indicates the this server requires mutual authentication
	// +optional
	CertificateAuth map[string]authtls.Config
	//DestinationPortPeerAuthenticationMode is mTLS mode of each port, port of upstream
	DestinationPortPeerAuthenticationMode map[string]PeerAuthenticationMode
	JwtAuthenticationOfPort               map[string]jwt.JWT
}
